home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 1
/
Nebula One.iso
/
Utilities
/
Printer
/
djf_for_3.0
/
printpage.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-10-18
|
19KB
|
813 lines
/*
* Beginn der Deskjet 500 Codierung;
* Hier erstmal alle statischen Variablen zur Compression
* werden nur von PrintPage() und PrintScanLine() benutzt
*/
#include <limits.h>
#include <stdio.h>
#ifdef __ARCHITECTURE__
#include <mach/cthreads.h>
#else
#include <cthreads.h>
#endif
#include <windowserver/printmessage.h>
#define MIN(a,b) ((a)<(b)?(a):(b))
#define sendsequence(s) (void) fwrite(s, 1, sizeof(s), stdout)
#define zerostrip 1
static int in_mode; /* current mode */
static unsigned char *seed_row;
static unsigned char obuf1[1024], obuf2[1024];
static unsigned char *testrow = obuf1, *bestrow = obuf2;
static int testsize, bestsize;
static char page_init[] = { 27, '*', 'p', '0', 'x', 'p', '1', 'Y', 27, '*', 'r', '1', 'A' }; /* <e>*p0x1Y<e>*r1A gives yet another 1/8" 'headroom' */
static char page_exit[] = { 27, '*', 'r', 'B', 27, '&', 'l', '0', 'H' };
/*
* Compression Routines taken from pclcomp by Tony Parkhurst
* Email address: tony@sdd.hp.com -or- hp-sdd!tony
*/
/* Compression for mode 9 taken from ghostscript 2.5.2 */
/*
** Compress_0() does mode 0 compression (which is a no compression).
** always usable !!
*/
static int
Compress_0(src, dest, count)
unsigned char *src, *dest;
int count;
{
memcpy(dest, src, count);
if ( zerostrip )
while ( count && dest[count-1] == 0 )
count--;
return(count);
}
#ifdef USE_1
/*
******************************************************************************
**
** Compress_1() does PCL compression mode 1 on the data.
** This mode is run length encoding.
**
** Given an input byte count of n, then the worst case output size
** would be 2 * n.
**
******************************************************************************
*/
static int
Compress_1(src, dest, count)
unsigned char *src, *dest;
register int count;
{
unsigned char *outptr = dest, *inptr = src;
unsigned char data; /* data values to match */
unsigned char temp;
int repcount; /* repeat count */
/*
** "count" is the number of bytes in "src" to compress
** into "dest".
*/
while ( count )
{
data = *inptr++; /* get value to work with */
count--;
repcount = 0; /* no repeats yet */
/*
** Look for successive bytes that match "data".
*/
while ( count && *inptr == data )
{
repcount++;
inptr++;
count--;
}
/*
** Now if we are out of data (count == 0), then
** if the repeated byte was zero, then we can ignore it
** unless the user disabled zero stripping.
*/
if ( count == 0 && data == 0 && zerostrip )
break; /* done */
/*
** Now output the repeat count and the value.
**
** If the repeat count exceeds 255, then we must send
** more repeat-value pairs.
**
** Each time thru the loop, repcount needs to be decremented
** to keep in sync. That is, if repcount == 256, then the
** first time thru the loop, <255> <data> are output, then
** repcount is now 1. So the next time thru the loop,
** repcount needs to be adjusted to 0 so that the next pair
** is <0> <data>. Thus, 1 data plus 255 repeats of data
** plus 1 data + 0 repeats of data ==> 257 total bytes
** of "data", which is what a repcount of 256 means.
*/
do
{
temp = MIN(repcount, 255);
*outptr++ = temp;
repcount -= temp;
*outptr++ = data;
} while ( repcount-- );
}
return ( outptr - dest ); /* size of compressed data */
}
#endif
#ifdef USE_2
/*
******************************************************************************
**
** Compress_2() does PCL compression mode 2 on the data.
** This mode is a combination of modes 0 and 1.
**
** Given an input byte count of n, then the worst case output size
** would be n + (n + 127)/128.
**
******************************************************************************
*/
static int
Compress_2(src, dest, count)
unsigned char *src, *dest;
register int count;
{
unsigned char *outptr, *inptr;
unsigned char *saveptr;
unsigned char data; /* data byte */
unsigned char lastbyte; /* last byte */
int repcount; /* repeat count */
int litcount; /* literal count */
/*
** src points to the input data.
** dest points to the output buffer.
** count is the number of input bytes.
*/
inptr = src;
outptr = dest;
/*
** Start loop thru data. Check for possible repeat at beginning.
*/
while ( count )
{
data = *inptr++; /* get value to work with */
count--;
repcount = 0; /* no repeat count yet */
/*
** Check for repeat, since we are not in the middle
** of a literal run, it does not have to be more than
** two bytes of similar data.
*/
while ( count && *inptr == data )
{
repcount++;
inptr++;
count--;
}
/*
** Now, if we are out of data (count == 0), then
** if the repeated byte was zero, then ignore it
** completely (don't bother outputing the trailing zeros).
**
** To always strip zero's, simply remove the "zerostrip"
** from the test.
*/
if ( count == 0 && data == 0 && zerostrip)
break; /* done */
/*
** If there was a repeat (repcount > 0), then we
** can output the command here, otherwise, we
** need to go into literal run mode.
**
** Note: This is a while loop because the repeat count
** may actually be greater than 127.
*/
if ( repcount >= 1 ) /* repeat mode */
{
while (repcount > 127)
{
*outptr++ = 129; /* count 127 */
*outptr++ = data; /* value */
repcount-= 128; /* offset */
}
if (repcount > 0)
{
*outptr++ = 256 - repcount; /* count */
*outptr++ = data; /* value */
/*
** Now pop to the top of the loop
** looking for more repeat counts.
*/
continue; /* top of loop */
}
/*
** Special case: If we have arrived at this point,
** then repcount is now equal to 0. This means
** that when we entered this section, repcount
** was a multiple of 128 (i.e. 128 :-).
**
** This means that there were 129 identical bytes,
** so the output does a replicate of 127 which
** gives 128 bytes, and we now have one byte left
** over which should NOT be output as a repeat
** run, rather it should be merged into the following
** literal run (if it exists).
**
** So, we will simply fall thru to the next section
** of code which assumes that we are working on
** a literal run.
*/
}
/*
** Literal run: At this point, the current data byte
** does NOT match the following byte. We will transfer
** these non-identical bytes until:
**
** 1) we run out of input data (count == 0).
** 2) we run out of room in this output block (128)
** 3) we come across a value which occurs at least
** three times in a row. A value occuring only
** twice in a row does NOT justify dropping
** out of a literal run.
**
** Special case: If we run out of room in the output block
** (which is 128 bytes), the last two values are the same,
** AND there is more input, it makes sense to restart
** the repeat detector in case the following bytes are
** repeats of the two. A simple check of the following
** byte will determine this.
** (This case falls out with the test for triples below).
**
** Special case: If we run out of room in the output block
** (which is 128 bytes), the last value is the same as
** the next one on the input, then it is better to let
** that byte be used in a possible replicate run following
** the literal run. If the last byte matches ONLY the
** following byte, (and not the one after that,) it is
** a wash, but for best results, we will test the
** following two bytes.
**
*/
litcount = 0;
saveptr = outptr++; /* save location of the command byte */
*outptr++ = data; /* save the first byte. */
lastbyte = data; /* remember for testing */
while ( count && litcount < 127 )
{
data = *inptr++;
count--;
litcount++;
*outptr++ = data;
/*
** Test to see if this byte matched the last one.
** If so, check the next one for a triple.
*/
if ( lastbyte == data && count && *inptr == data )
{
/*
** We have a triple, adjust accordingly.
**
** Add two bytes back onto the input.
*/
count += 2;
inptr -= 2;
outptr -= 2;
litcount -= 2;
break; /* out of loop */
}
lastbyte = data; /* save data byte */
}
/*
** Check the special case number 2 above.
*/
if ( litcount == 127 && count > 1 && data == *inptr
&& data == inptr[1] )
{
/* Restore the last byte to the input stream */
count += 1;
inptr -= 1;
outptr -= 1;
litcount -= 1;
}
/*
** Save the literal run count.
*/
*saveptr = litcount;
/*
** Now go back to the top and look for repeats.
*/
}
count = outptr - dest; /* for return value */
return ( count );
}
#endif
#ifdef USE_3
/*
******************************************************************************
**
** Compress_3() does PCL compression mode 3 on the data.
** This mode is delta row encoding.
**
** Given an input byte count of n, then the worst case output size
** would be n + (n + 7)/8
**
******************************************************************************
*/
static int
Compress_3(seed, new, dest, count)
unsigned char *seed, *new, *dest;
register int count;
{
unsigned char *sptr=seed, *nptr=new, *dptr=dest;
unsigned char *saveptr;
int delta, xfer;
unsigned char command;
/*
** "count" is the number of bytes of data in "new" that need to
** be compressed into "dest" using "seed" as the basis for diffs.
*/
while ( count > 0 )
{
delta = 0; /* position counter */
/*
** Hunt through the data until the new data is different
** from the seed data.
*/
while ( *sptr == *nptr && delta < count )
{
delta++;
sptr++;
nptr++;
}
/*
** Either a difference has been found, or we ran out of data.
*/
if ( delta >= count ) /* too far */
break; /* done */
count -= delta;
/*
** Now set up the output with the command byte[s].
** (leaving the actual byte copy count till last.)
*/
/*
** The first command byte can only hold up to 31.
** If the delta is larger, then following bytes will
** be needed.
*/
saveptr = dptr++; /* save the address for command byte */
command = MIN(delta, 31);
/*
** Any remaining count follows.
**
** If count is 31, then a following byte must be given,
** even if 0. Same holds if 255 is given in succesive bytes.
*/
if ( command == 31 )
{
delta -= command; /* adjust for first count */
do {
xfer = MIN(delta,255);
delta -= xfer;
*dptr++ = xfer;
} while ( xfer == 255 );
}
/*
** Now transfer up to 8 bytes, stopping when the new byte
** matches the seed byte. One could make a case for
** transfering a matching byte too (if stuck in the middle
** of the 8 bytes), but it does not impact the worst case,
** and in the long run, the compression will not be as good.
** Also, we need to make sure that we don't overrun count.
** ("count" is checked first so we don't reference past the
** end of the memory block).
*/
for ( xfer = 0;
count && *sptr != *nptr && xfer < 8;
xfer++)
{
*dptr++ = *nptr++; /* transfer byte */
sptr++; /* bump seed pointer too */
count--;
}
/*
** Now xfer is the number of bytes transfered, but the
** number range is 3 bits (0-7), so decrement and merge
** it into the command byte and put it in the data.
*/
command += ((xfer - 1) << 5);
*saveptr = command;
}
return ( dptr - dest );
}
#endif
#ifdef USE_9
/*
******************************************************************************
**
** Compress_9() does PCL compression mode 9 on the data.
** This mode is delta row encoding.
**
** Given an input byte count of n, then the worst case output size
** would be ???
**
******************************************************************************
*/
static int
Compress_9(previous, current, compressed, bytecount)
unsigned const char *previous, *current;
unsigned char *compressed;
register int bytecount;
/*
* Mode 9 2D compression for the HP DeskJet 500C. This mode can give
* very good compression ratios, especially if there are areas of flat
* colour (or blank areas), and so is 'highly recommended' for colour
* printing in particular because of the very large amounts of data which
* can be generated
*/
{
register const unsigned char *cur = current;
register const unsigned char *prev = previous;
register unsigned char *out = compressed;
const unsigned char *end = current + bytecount;
while (cur < end) { /* Detect a run of unchanged bytes. */
const unsigned char *run = cur;
register const unsigned char *diff;
int offset;
while (cur < end && *cur == *prev) {
cur++, prev++;
}
if (cur == end)
break; /* rest of row is unchanged */
/* Detect a run of changed bytes. */
/* We know that *cur != *prev. */
diff = cur;
do {
prev++;
cur++;
} while (cur < end && *cur != *prev);
/* Now [run..diff) are unchanged, and */
/* [diff..cur) are changed. */
offset = diff - run;
{
const unsigned char *stop_test = cur - 4;
int dissimilar, similar;
while (diff < cur) {
const unsigned char *compr = diff;
const unsigned char *next; /* end of run */
unsigned char value=0;
while (diff <= stop_test &&
((value = *diff) != diff[1] ||
value != diff[2] ||
value != diff[3]))
diff++;
/* Find out how long the run is */
if (diff > stop_test) /* no run */
next = diff = cur;
else {
next = diff + 4;
while (next < cur && *next == value)
next++;
}
#define MAXOFFSETU 15
#define MAXCOUNTU 7
/* output 'dissimilar' bytes, uncompressed */
if ((dissimilar = diff - compr)) {
int temp, i;
if ((temp = --dissimilar) > MAXCOUNTU)
temp = MAXCOUNTU;
if (offset < MAXOFFSETU)
*out++ = (offset << 3) | (unsigned char) temp;
else {
*out++ = (MAXOFFSETU << 3) | (unsigned char) temp;
offset -= MAXOFFSETU;
while (offset >= 255) {
*out++ = 255;
offset -= 255;
}
*out++ = offset;
}
if (temp == MAXCOUNTU) {
temp = dissimilar - MAXCOUNTU;
while (temp >= 255) {
*out++ = 255;
temp -= 255;
}
*out++ = (unsigned char) temp;
}
for (i = 0; i <= dissimilar; i++)
*out++ = *compr++;
offset = 0;
} /* end uncompressed */
#define MAXOFFSETC 3
#define MAXCOUNTC 31
/* output 'similar' bytes, run-length encoded */
if ((similar = next - diff)) {
int temp;
if ((temp = (similar -= 2)) > MAXCOUNTC)
temp = MAXCOUNTC;
if (offset < MAXOFFSETC)
*out++ = 0x80 | (offset << 5) | (unsigned char) temp;
else {
*out++ = 0x80 | (MAXOFFSETC << 5) | (unsigned char) temp;
offset -= MAXOFFSETC;
while (offset >= 255) {
*out++ = 255;
offset -= 255;
}
*out++ = offset;
}
if (temp == MAXCOUNTC) {
temp = similar - MAXCOUNTC;
while (temp >= 255) {
*out++ = 255;
temp -= 255;
}
*out++ = (unsigned char) temp;
}
*out++ = value;
offset = 0;
} /* end compressed */
diff = next;
}
}
}
return out - compressed;
}
#endif
/*
* print a scanline and select the modes
*/
void PrintScanLine(unsigned char *buf, int anz)
{
int minmode = 0;
unsigned char *temprow;
/* Anything is better than this */
bestsize = INT_MAX;
#ifdef USE_9
testsize = seed_row ? Compress_9(seed_row, buf, testrow, anz)
: INT_MAX;
if (testsize + (in_mode != 9 ? 2 : 0) < bestsize) {
temprow = testrow;
testrow = bestrow;
bestrow = temprow;
bestsize = testsize;
minmode = 9;
}
#endif
#ifdef USE_3
testsize = seed_row ? Compress_3(seed_row, buf, testrow, anz)
: INT_MAX;
if (testsize + (in_mode != 3 ? 2 : 0) < bestsize) {
temprow = testrow;
testrow = bestrow;
bestrow = temprow;
bestsize = testsize;
minmode = 3;
}
#endif
#ifdef USE_2
/* mode 2 is always tested */
testsize = Compress_2( buf, testrow, anz);
if (testsize + (in_mode != 2 ? 2 : 0) < bestsize) {
temprow = testrow;
testrow = bestrow;
bestrow = temprow;
bestsize = testsize;
minmode = 2;
}
#endif
#ifdef USE_1
/* Usually don't check mode 1 (it is rarely better than mode 2) and */
/* mode 0 (mode 2 is only about 1% longer in worst case) */
testsize = Compress_1( buf, testrow, anz);
if (testsize + (in_mode != 1 ? 2 : 0) < bestsize) {
temprow = testrow;
testrow = bestrow;
bestrow = temprow;
bestsize = testsize;
minmode = 1;
}
#endif
/* Mode 0 is cheap and always there */
testsize = Compress_0(buf, testrow, anz);
if (testsize + (in_mode != 0 ? 1 : 0) < bestsize) {
bestrow = buf;
bestsize = testsize;
minmode = 0;
}
if ( in_mode != minmode ) {
printf(minmode ? "%dm" : "m", minmode);
in_mode = minmode;
}
/* <esc>*b has already been output */
printf(bestsize ? "%dw" : "w", bestsize);
if ( fwrite( bestrow, 1, bestsize, stdout) < bestsize ) {
/* check for error and exit */
if ( ferror(stdout) )
{
perror("Output error");
exit(-2);
}
}
}
/*
* small improvement for speed:
* if we know that we are printing (one of the pins 0-49 is active) it is
* faster to print a null line, than skipping it via *b%dY. This way the
* printing head doesn't need to print, move vertically and restart printing.
*/
#define PINS 50
void PrintPage(void)
{
/* output the whole Page to the printer */
register unsigned char *sp, *p;
register int i;
extern NXPrintPageMessage ppm;
extern mutex_t mutex1;
int in_seq = -1, empty_lines = 0;
mutex_lock(mutex1);
sendsequence(page_init); /* start plot at current position */
mutex_unlock(mutex1);
in_mode = -1; /* invalidate some vars */
seed_row = NULL;
printf("\033*b"); /* prepare */
for (i = ppm.pixelsHigh, sp = (unsigned char *) ppm.printerData;
i > 0; i--, sp += ppm.bytesPerRow) {
/*
#ifdef DEBUG
fprintf(stderr, "Line: %d/%d\n", ppm.pixelsHigh - i, ppm.pixelsHigh);
#endif
*/
/* Im Druck einer 50er Zeile? */
if (in_seq >= 0) {
mutex_lock(mutex1);
PrintScanLine(sp, ppm.bytesPerRow);
mutex_unlock(mutex1);
/* Ende der ganzen Zeile? */
if (++in_seq == PINS)
in_seq = -1;
}
else {
for (p = sp + ppm.bytesPerRow - 1; p >= sp && *p == 0; p--)
;
/* Leere Zeile? */
if (p < sp)
empty_lines++;
else {
/* Volle Zeile nach leeren Zeilen */
if (empty_lines > 0) {
(void) fprintf(stdout, "%dy", empty_lines);
}
mutex_lock(mutex1);
PrintScanLine(sp, ppm.bytesPerRow);
mutex_unlock(mutex1);
empty_lines = 0;
/* Erster Pin */
in_seq = 0;
}
}
seed_row = sp;
}
printf("M"); /* Must have one capital letter */
mutex_lock(mutex1);
sendsequence(page_exit); /* Eject page */
mutex_unlock(mutex1);
}